/**
 *  @file CppMemPool.h
 *
 *  C++ interface for auto release pool
 */
#ifndef _CPPMEMPOOL_H_
#define _CPPMEMPOOL_H_

#include <cstddef>
#include <new>
#include "MemReleasePool.h"
#include "NoCopyAndNew.h"

namespace cppflib
{

namespace cppmempool
{

_DLLAPI bool GlobalInit();
_DLLAPI bool GlobalFinish();

_DLLAPI memrelpool_t * CreatePool();
_DLLAPI void FreePool(memrelpool_t * pRelPool);
_DLLAPI bool UnlinkFromGroup(memrelpool_t * pRelPool);
_DLLAPI bool AddAsCurrentPool(memrelpool_t * pRelPool);
_DLLAPI int GetPoolNodesCount(memrelpool_t * pRelPool);
_DLLAPI int GetPoolsCount();

_DLLAPI int Release(void * pMemLoc);
_DLLAPI int Retain(void * pMemLoc);
_DLLAPI void * UnlinkFromPool(void * pMemLoc);
_DLLAPI bool AddToPool(void * pMemLoc, memrelpool_t *pRelPool);
_DLLAPI bool AddToCurPool(void * pMemLoc);
_DLLAPI bool AddToDefaultPool(void * pMemLoc);

/**
 *  Declare a type for those placement new below
 */
typedef enum _cmparg
{
   NEWCLASS,   ///< for allocating instances derived from BaseObject
   NEWPTR      ///< for allocating other pointer/array of primitive types (e.g. int, char, ...) or array of pointers
} cmparg_t;

/**
 *  A friendly wrapper class to use a memory pool within a scope.
 *
 *  Note: It is intended to be used as a local variable
 *
 *  e.g.
 *
 *  // Create a new memory pool which will be freed when function returns
 *  void funcA(void) 
 *  {
 *     MemPoolWrap newPool;
 *     
 *       ...
 *       ...
 *  }
 *
 *  // use a foreign pool and will be freed when function returns
 *  void funcB(memrelpool_t * pMemPool) 
 *  {
 *     MemPoolWrap newPool(pMemPool);
 *     
 *       ...
 *       ...
 *  }
 *
 */
class _DLLAPI MemPoolWrap : public NoCopyAndNew
{
private:
   memrelpool_t *pMemPool;

public:
   /**
    *  Create a new pool in default ctor
    */
   MemPoolWrap()
   {
      pMemPool = cppmempool::CreatePool();
   }

   explicit MemPoolWrap(memrelpool_t *pMemPool)
   {
      this->pMemPool = pMemPool;
   }

   int GetNodesCount()
   {
      return cppmempool::GetPoolNodesCount(this->pMemPool);
   }

   bool AddMemBlock(void * pMemLoc)
   {
      return cppmempool::AddToPool(pMemLoc, this->pMemPool);
   }

   /**
    *  Free this pool explicitly
    */
   void Close()
   {
      if (this->pMemPool) {
         cppmempool::FreePool(this->pMemPool);
         this->pMemPool = NULL;
      }
   }

   ~MemPoolWrap()
   {
      Close();
   }
};
   


}  // end of cppmempool

}  // end of cppflib

/**
 *  Macro to throw exception in constructor (call 'ReleaseBeforeThrowing' before throwing)
 *  Those classes should have a non-virtual 'ReleaseBeforeThrowing' method before using this macro
 */
#define THROW_CTOR_EXCEP_CODE(EXPNAME, ERRMSG, ERRCODE) \
    do { \
       this->ReleaseBeforeThrowing(); \
       throw EXPNAME(ERRMSG, ERRCODE); \
    } while(false)

/**
 *  Macro to throw exception in constructor (call 'ReleaseBeforeThrowing' before throwing)
 *  Those classes should have a non-virtual 'ReleaseBeforeThrowing' method before using this macro
 */
#define THROW_CTOR_EXCEP(EXPNAME, ERRMSG) \
    do { \
       this->ReleaseBeforeThrowing(); \
       throw EXPNAME(ERRMSG, -1); \
    } while(false)


/**
 *  Global placement new to allocate memory in C++ way
 *  To allocate memory for a class instance, the class must be derived from BaseObject
 *
 *  Important note:
 *  Allocating an array of objects like this
 *
 *        MyObject *pArray = new (NEWCLASS) MyObject[4];
 *
 *  is *NOT* supported.
 *
 *  Instead, allocate an array of pointers to MyObject and 'new' each slot like this
 *
 *        MyObject **pArray = new (NEWPTR) MyObject * [4];
 *        for (int i = 0 ; i < 4; ++i)
 *           pArray[i] = new (NEWCLASS) MyObject();
 *
 *  To release them explicitly, use cppmempool::Release instead of the traditional 'delete'
 *
 *        for (int i = 0 ; i < 4; ++i)
 *           cppmempool::Release(pArray[i]);
 *        cppmempool::Release(pArray);
 */
_DLLAPI void * operator new(size_t size, cppflib::cppmempool::cmparg_t cppmemsig);
_DLLAPI void * operator new[](size_t size, cppflib::cppmempool::cmparg_t cppmemsig);

/**
 *  For classes or object that wish to provide their own destructor function before being freed.
 *
 *  Note: Since dtorFunc is passed in the original pointer to a piece of memory, dtorFunc must NOT
 *        free the pointer as it will be done so after the function returns. dtorFunc only gives
 *        a chance to user to do some clean up job before it is freed.
 *
 *  e.g. for a class which is not derived from BaseObject but would like to be added to
 *       to release pool, write a dtorFunc like this:
 *
 *       void MyDestroyFunc(void *p)
 *       {
 *          MyCustomClass *t = (MyCustomClass*)p;
 *          t->~MyCustomClass();  // call the destructor explicitly but don't delete 't' here
 *       }
 */
_DLLAPI void * operator new(size_t size, dtorfunc_t dtorFunc);

/**
 *  These are NOT intended to be used explicitly. Use cppmempool::Release instead to delete an instance
 */
_DLLAPI void operator delete(void *p, cppflib::cppmempool::cmparg_t cppmemsig);
_DLLAPI void operator delete[](void *p, cppflib::cppmempool::cmparg_t cppmemsig);
_DLLAPI void operator delete(void *p, dtorfunc_t dtorFunc);

#ifdef _DEBUG
// override these in debug mode (these can only be overriden in static library)

#  if defined(__GNUC__)
void * operator new(size_t size) throw (std::bad_alloc);
void * operator new[](size_t size) throw (std::bad_alloc);
void operator delete(void *p) throw ();
void operator delete[](void *p) throw ();
#  else
void * operator new(size_t size);
void * operator new[](size_t size);
void operator delete(void *p);
void operator delete[](void *p);
#  endif
#endif

#endif
